This architectural blueprint outlines how multi-tenancy rules, dynamic permission evaluation, global action filters, and automated background workers operate synchronously within SaaSKit.Documentation Index
Fetch the complete documentation index at: https://codevestllc.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
🛂 1. RBAC & Dynamic Permission Engine
SaaSKit utilizes an action-based, highly granular Role-Based Access Control (RBAC) system. Instead of checking static roles inside endpoints, the system enforces individual permission capabilities evaluated dynamically across changing tenant boundaries.🔑 Machine-Safe Key Generation
To keep data consistency, all human-readable roles and permissions are processed through an explicit key normalization pipeline before database insertion.- Converts strings to lowercase.
- Normalizes Unicode sequences and strips accent marks / diacritics.
- Converts spaces and special characters into clean underscores (
_). - Collapses repeated underscores and trims edge parameters.
| Human Readable Input | Machine-Safe Key Output |
|---|---|
Access To Admin | access_to_admin |
Email Settings Read | email_settings_read |
Add Car | add_car |
Tickets Send Support Ticket | tickets_send_support_ticket |
🚨 Architectural Naming Convention Constraint
There is a strict, critical distinction between seed-level authorization configurations and dynamically generated runtime permissions:- DbSeeder Permissions: Initial infrastructural permissions seeded programmatically directly into the data source utilize dot notation boundaries (e.g.,
email_templates.read,subscription.upgrade,stripe.checkout.create). - Dashboard-Created Permissions: Any permission added dynamically via the administrator dashboard GUI will strictly replace spaces/special characters with underscores (
_). No dot notation is permitted during frontend configuration injections.
Add Car from the web dashboard panel, the system normalizes the key strictly to add_car. Consequently, the backend controller endpoint protecting that resource must leverage matching architecture:
[HttpPost(“addCar”)]
[Authorize(Policy = “add_car”)]
🌱 Infrastructure Seeding & Permission Scoping
During the database instantiation sequence managed by DbSeeder, core application components, static layout frameworks, and identity foundations are securely provisioned. 👥 System Role Foundations The application instantiates two immutable system roles linked directly to the master host workspace (00000000-0000-0000-0000-0000000000ff): Super Admin (super_admin): Granted global administrative capability via the master bypass token (admin.full_access). Admin (admin): Assigned a comprehensive bundle of tenant-level operational claims. 🛡️ Permission Scope Allocation Rules Every permission registered inside the system maps directly to an explicit PermissionScope enumerator configuration, which controls availability layout boundaries: PermissionScope.System: These permissions are strictly confined to system users operating under the master global realm (e.g., plan.create, tenants.read, features.create). They cannot be visualized, assigned, or manipulated by standard tenant administrators. PermissionScope.Tenant: Cross-compatible capabilities available to both standard tenant environments and the system workspace (e.g., user.read, role.create, invoice.read).🎯 2. Global Action Filters & Interceptors
Cross-cutting system concerns, immutable security auditing, activity tracing, and real-time user alerting are handled completely via ASP.NET Core Action Filters. These filters run globally but trigger dynamically based on custom attributes placed over endpoint methods. 🔐 [AuditLog] Filter This filter creates an unalterable paper trail of critical data mutations. Attribute Parameters: [AuditLog(“Action Description”, EntityName = “TargetEntity”)] First Parameter: Declares the explicit description of the mutation (e.g., “Create Role”). EntityName: Defines the structural entity type being modified (e.g., “Role”). Hierarchical Settings Check: Before writing to the database, it queries the active workspace’s AdvancedSettings. If audit logging is disabled for that specific tenant, it immediately checks the global System Settings. If the system switch is enabled but the tenant has it turned off, the log is written globally but marked as decoupled from that specific tenant (TenantId = null). Execution Boundary: It prepares a pending log data structure before execution but only commits to the database if the endpoint returns a successful 2xx HTTP status code. If the request throws an exception or returns a client/server error (4xx/5xx), the log transaction is safely aborted. 👤 [ActivityLog] Filter Tracks user interactions and behaviors for security auditing and session history tracing. Attribute Parameters: [ActivityLog(“Action Description”, EntityName = “TargetEntity”)] (e.g., [ActivityLog(“User Login”, EntityName = “User”)]). Metadata Enrichment: Beyond baseline identity tracking, this filter extracts environmental client fingerprints directly from the current HttpContext connection headers: IP Address: Resolves and maps the user’s remote IP network location. User-Agent: Parses and writes the full browser, framework, and OS signature string. 🔔 [Notification] Filter A globally registered filter that automatically produces application-level alerts following successful system changes without polluting business services. Execution Guard: If the invoked endpoint does not contain the [Notification] attribute, the filter skips processing. If present, it waits for a successful 2xx HTTP response with no uncaught exceptions before compiling the payload. Context Resolution Routing: TenantId Resolution Priority: Maps via IHasTenant argument -> UseEntityIdAsTenantId data extraction -> ITenantIdProvider.CurrentTenantId context fallback. UserId Resolution Priority: Maps via RecipientRouteKey route parameters -> defaults to the actively authenticated caller. ⚙️ Attribute Configuration Properties The [Notification] attribute exposes specialized configurations to dynamically alter the text layout, targeting, and contextual routing during the intercept phase: Title: Explicitly defines the primary header of the notification. If omitted, the system falls back onto a default baseline header. Message: Explicitly sets the main notification body text. If this parameter is left unconfigured, the filter automatically scans the incoming HTTP response model properties looking for message or Message keys. If no body content is resolved from the payload, it sets the message value identical to the Title. Type: Maps the visual classification variant utilizing the NotificationType structural enumerator state (e.g., NotificationType.Success, NotificationType.Error, NotificationType.Warning). IsTenantScoped: Boolean flag managing systemic delivery context. When configured as true, it flags the pipeline that the notice belongs to the collective workspace rather than an isolated persona. RecipientRouteKey: Used when IsTenantScoped = false. It instructs the filter to capture a specific parameter string from the route arguments or incoming method signatures (such as “userId”) to track down the targeted user identity. UseEntityIdAsTenantId: Overrides ambient tenant discovery. Forces the filter to capture the unique tracking ID from the mutated data model or response structure and apply that parameter as the active destination TenantId. TitleResultProperty: Instructs the engine to dynamically extract the notification header title text straight out of the successful HTTP response body properties at runtime.🗂️ Key Core Notification Scopes & Scenarios
The database state behavior changes dynamically depending on the resolved payload matrix:| Resolved UserId | Resolved TenantId | Architectural Behavior & Scope |
|---|---|---|
| null | Guid | Broadcast Tenant-Wide: The notification targets the entire tenant organization. Every user currently attached to that specific workspace will receive and visualize the alert. |
| Guid | Guid | Tenant-Scoped User Alert: Targets an individual user strictly inside that specific tenant workspace. The alert persists only while operating under that workspace boundary. |
| Guid | null | Global User-Specific Alert: Private user alert decoupled from any active workspace. Follows the user identity globally across the entire platform ecosystem. |
- Default Behavior (Dispatches directly to the caller within active context)
- Tenant-Wide Broadcast (Dispatches to everyone in the workspace, setting UserId to null)
- Targeted Route Recipient (Dispatches to an individual user defined in route parameter variables)
- New Entity as Tenant Boundary (Extracts the newly generated entity ID as the target TenantId parameter)
- Dynamic Title From Response Payload (Reads the successful response body properties to map the client header dynamically)
🛡️ 3. System Guard Filters (Endpoint Barriers)
SaaSKit protects sensitive pipelines from runtime infrastructure failures by placing targeted guard attributes over controller methods. These filters run during the authorization phase and short-circuit requests with a 403 Forbidden object if environment setups are incomplete. [BillingEnabled] Inspects the platform’s global AdvancedSettings. If EnableBilling is configured as false, the filter blocks request processing instantly to avoid broken workspace billing states. [StripeEnabled] Evaluates active gateway states. If EnableStripe is false or secret environment gateway keys are missing, all lower financial pipeline triggers are gracefully blocked. [StorageSettingsRequirement] Monitors active file stream uploads (IFormFile). If the core integration mapping profile returns isConnected == false, operations are blocked before file storage client initialization failures can trigger crash dumps. [EmailSettingsRequirement(SystemSettingsAllowed = true)] Guards automated communication dispatch routes (e.g., invitation paths). If the active tenant workspace has not configured an independent SMTP/API mail node, setting SystemSettingsAllowed = true permits a safe fallback to the master application node. Otherwise, the request is denied to prevent silent mail drops.⚙️ 4. Enterprise Background Services (Hosted Workers)
To maximize runtime throughput and maintain database consistency without blocking the synchronous HTTP response loop, SaaSKit relies on dedicated IHostedService long-running workers.
🏢 5. Tenant Provisioning & Subscription Onboarding
The onboarding pipeline manages infrastructure binding and subscription initialization when a new workspace is provisioned.🛂 Automated Admin Role Assignment
When a user provisions a new tenant workspace and initiates a subscription sequence, the core engine automatically assigns the Admin (admin) role to that user. This ensures the onboarding creator immediately obtains full operational permission claims and management control inside their newly isolated tenant boundary.
💳 Stripe Checkout & Renewal Plan Customization
Within theStripeService layer, the checkout routing governs the subscription trial initialization:
- Default Workflow Execution: Inside the
CreateTrialCheckoutSessionAsyncfunction, the setup programmatically targets the pre-configured"Standard"plan as the automated renewal plan at the end of the free trial period. - Developer Customization Scope: This configuration is completely flexible. If you create alternative structures, pricing tiers, or customized tiers, you can easily change or swap this target plan inside the service methods to match your specific application ecosystem requirements.
🗄️ Multi-Tenant Data Isolation (EF Core Query Filters)
When extending the platform infrastructure or implementing custom custom entities, you must enforce strict database-level boundaries to prevent data leaks across workspaces. SaaSKit handles this natively via Entity Framework Core Global Query Filters. If your new entity is tenant-specific, you MUST configure the automated tenancy evaluation filter inside yourApplicationDbContext initialization pipeline: